import requests
from lxml import etree
from typing import List
from io import StringIO
import re
import time

M3U8_RE = re.compile(r'https://.*?.m3u8')
MP4_RE = re.compile(r'https://.*?.mp4')
session = requests.Session()
BASE_URL = 'https://www.nfmovies.com'
session.headers.update({
    'authority':
    'www.nfmovies.com',
    'sec-ch-ua':
    '" Not;A Brand";v="99", "Microsoft Edge";v="91", "Chromium";v="91"',
    'upgrade-insecure-requests':
    '1',
    'accept':
    'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
    'sec-fetch-site':
    'same-origin',
    'sec-fetch-mode':
    'navigate',
    'sec-fetch-user':
    '?1',
    'sec-fetch-dest':
    'document',
    'accept-language':
    'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
})

mp4_session = requests.Session()
mp4_session.headers.update({
    'Connection': 'keep-alive',
    'sec-ch-ua':
    '" Not;A Brand";v="99", "Microsoft Edge";v="91", "Chromium";v="91"',
    'DNT': '1',
    'sec-ch-ua-mobile': '?0',
    'Accept': '*/*',
    'Sec-Fetch-Site': 'cross-site',
    'Sec-Fetch-Mode': 'no-cors',
    'Sec-Fetch-Dest': 'video',
    'Referer': 'https://www.nfmovies.com/',
    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
    'Range': 'bytes=0-'
})


def _make_name(name):
    return name.replace('/', '_').replace(' ', '_')


def _get(*args, **kwargs):
    for i in range(3):
        try:
            resp = session.get(*args, **kwargs)
        except Exception as _:
            time.sleep(2)
            continue
        if resp.status_code != 200:
            time.sleep(1)
            continue
        return resp
    return None


def _realurl(url):
    resp = mp4_session.get(url, allow_redirects=False)
    if resp.status_code == 200:
        return url
    if resp.status_code in (302, 301):
        return resp.headers.get('Location')
    return None


def _get_tree(resp):
    return etree.parse(StringIO(resp.text), etree.HTMLParser())


class Video(object):
    def __init__(self, name: str, url: str):
        self.name = _make_name(name)
        self._detail_url = url
        self._url = None

    @property
    def url(self):
        if self._url:
            return self._url
        # 获取播放链接
        resp = _get(BASE_URL + self._detail_url)
        if not resp:
            return None

        res = M3U8_RE.findall(resp.text)
        if not Video._valid_url(res):
            res = MP4_RE.findall(resp.text)
        if not Video._valid_url(res):
            return None

        self._url = res[0]
        if self._url.endswith('mp4'):
            self._url = _realurl(self._url)
        return self._url

    @staticmethod
    def _valid_url(urls):
        return urls and urls[0].find(')') < 0


class Source(object):
    def __init__(self, name: str, eles: List[etree._Element]):
        self.name = _make_name(name)
        self.videos = [
            Video(e.attrib['title'], e.attrib['href']) for e in eles
        ]


class QueryItem(object):
    def __init__(self, ele: etree._Element):
        # 解析标题
        title = ele[0][0]
        self.name = _make_name(title.text)
        self._url = title.attrib.get('href')

        # 解析其它
        self.directors = []
        self.actors = []
        self.desc = ""
        self._sources = []

        for p in ele[1:-1]:
            c = p.xpath('span[@class="text-muted"]')
            if not c:
                continue
            c = c[0]
            if c.text.startswith('导演'):
                if c.tail:
                    self.directors = c.tail.strip().split('/')
            elif c.text.startswith('主演'):
                self.actors = [a.text for a in p.xpath('a') if a.text]
            elif c.text.startswith('简介'):
                if c.tail:
                    self.desc = c.tail.strip()

    @property
    def sources(self):
        if self._sources:
            return self._sources
        resp = _get(BASE_URL + self._url)
        if not resp:
            return None
        tree = _get_tree(resp)
        eles = tree.xpath(
            '//div[contains(@class, "myui-panel-box") and contains(@class, "clearfix")]'
        )
        if not eles:
            return None
        ele = eles[0]

        # 获取片名
        try:
            names = [e.text for e in ele[0][0].xpath('ul/li/a')]
        except Exception as _:
            return None

        # 获取播放列表
        video_eles = [e.xpath('ul/li/a') for e in ele[1].getchildren()]
        for i, v in enumerate(names):
            self._sources.append(Source(v, video_eles[i]))

        return self._sources


def query(name: str) -> List[QueryItem]:
    params = {'searchword': name}
    resp = _get(BASE_URL + '/search.php', params=params)
    if not resp:
        return None
    tree = _get_tree(resp)
    eles = tree.xpath('//ul[@id="searchList"]//div[@class="detail"]')
    return [QueryItem(e) for e in eles]
